%%%-------------------------------------------------------------------
%%% @author Anil Kumar S. R. <sranil@gmail.com>
%%% @copyright (C) 2010, 
%%% @doc
%%%
%%% @end
%%% Created : 11 Oct 2010 by Anil Kumar S. R. <sranil@gmail.com>
%%%-------------------------------------------------------------------
-module(parse_sip_packet).
-export([parse/1]).
-include("../include/sip.hrl").

parse(Message) when is_binary(Message) ->
    parse(binary_to_list(Message));

parse(Message) ->
    Header_Content_Tokens = re:split(Message, "\r\n\r\n", [{return, list}]),
    Length = length(Header_Content_Tokens),
    if
        Length < 2 orelse Length > 2 ->
            #parse_record{};
        true ->
            [Header | Content] = Header_Content_Tokens,
            [Start_Line | Header_Tokens] = re:split(Header, "\r\n", [{return, list}]),
            if
                Content == [[]] ->
                    SDP = undefined;
                true ->
                    SDP = parse_sdp_data(#sdp{}, re:split(Content, "\r\n", [{return, list}]))
            end,
            parse_header(parse_start_line(#parse_record{sdp = SDP, content_length = 0}, Start_Line), Header_Tokens)
    end.

parse_start_line(R, Start_Line) ->
    Tokens = re:split(Start_Line, " ", [{return, list}]),
    if
        hd(Tokens) == "SIP/2.0" ->
            [_, Status_Code | _] = Tokens,
            R#parse_record{type = status, status = list_to_integer(Status_Code)};
        true ->
            [Method, _Request_URI | _] = Tokens,
            R#parse_record{type = request, method = Method}
    end.

parse_header(R, []) ->
    R;

parse_header(R, [Head|Tail]) ->
    case re:run(Head, ":", []) of
        {match, [{S1, L1}]} ->
            Header_Field_Name = string:strip(string:substr(Head, 1, S1), right),
            Value = string:strip(string:substr(Head, S1+L1+1), left),
            Header_Field_Value = re:split(Value, ";", [{return, list}]),
            case string:to_lower(Header_Field_Name) of
                "via" ->
                    Parse_Record = parse_header_values("via", R, Header_Field_Value),
                    parse_header(Parse_Record#parse_record{via = Parse_Record#parse_record.via ++ [Value]}, Tail);
                "from" ->
                    Parse_Record = parse_header_values("from", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "to" ->
                    Parse_Record = parse_header_values("to", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "call-id" ->
                    Parse_Record = parse_header_values("call-id", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "cseq" ->
                    Parse_Record = parse_header_values("cseq", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "contact" ->
                    Parse_Record = parse_header_values("contact", R, re:split(Value, ",", [{return, list}])),
                    parse_header(Parse_Record, Tail);
                "www-authenticate" ->
                    Parse_Record = parse_header_values("authenticate", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "proxy-authenticate" ->
                    Parse_Record = parse_header_values("authenticate", R, Header_Field_Value),
                    parse_header(Parse_Record, Tail);
                "record-route" ->
                    if
                        R#parse_record.type == status ->
                            parse_header(R#parse_record{route = lists:append(R#parse_record.route, [Value])}, Tail);
                        true ->
                            parse_header(R#parse_record{route = lists:append([Value], R#parse_record.route)}, Tail)
                    end;
                "content-length" ->
                    parse_header(R#parse_record{content_length = list_to_integer(hd(Header_Field_Value))}, Tail);
                _ ->
                    parse_header(R, Tail)
            end;
        _ ->
            parse_header(R, Tail)
    end.
                
parse_header_values(_, R, []) ->
    R;
parse_header_values("via", R, [H|T]) ->
    Key_Value = re:split(H, "=", [{return, list}]),
    if 
        length(Key_Value) == 1 ->
            [Version, IP_Port_Details] = re:split(hd(Key_Value), " ", [{return, list}]),
            if 
                Version == "SIP/2.0/UDP" ->
                    IP_Port = re:split(IP_Port_Details, ":", [{return, list}]),
                    if 
                        length(IP_Port) == 1 ->
                            parse_header_values("via", R#parse_record{received_ip = hd(IP_Port)}, T);
                        true ->
                            [IP, Port] = IP_Port,
                            parse_header_values("via", R#parse_record{received_ip = IP, rport = list_to_integer(Port)}, T)
                    end;
                true ->
                    parse_header_values("via", R, T)
            end;
        true ->
            case Key_Value of
                ["received", IP] ->
                    parse_header_values("via", R#parse_record{received_ip = IP}, T);
                ["rport", Port] ->
                    parse_header_values("via", R#parse_record{rport = list_to_integer(Port)}, T);
                ["branch", Branch_ID] ->
                    if
                        R#parse_record.branch_id == undefined ->
                            parse_header_values("via", R#parse_record{branch_id = Branch_ID}, T);
                        true ->
                            parse_header_values("via", R, T)
                    end;
                _ ->
                    parse_header_values("via", R, T)
            end
    end;
parse_header_values("from", R, [H|T]) ->
    Key_Value = re:split(H, "=", [{return, list}]),
    case Key_Value of
        ["tag", From_Tag] ->
            parse_header_values("from", R#parse_record{from_tag = From_Tag}, T);
        _ ->
            parse_header_values("from", R#parse_record{from = hd(Key_Value)}, T)
    end;
parse_header_values("to", R, [H|T]) ->
    Key_Value = re:split(H, "=", [{return, list}]),
    case Key_Value of
        ["tag", To_Tag] ->
            parse_header_values("to", R#parse_record{to_tag = To_Tag}, T);
        _ ->
            parse_header_values("to", R#parse_record{to = hd(Key_Value)}, T)
    end;
parse_header_values("call-id", R, [H|T]) ->
    parse_header_values("call-id", R#parse_record{call_id = H}, T);
parse_header_values("cseq", R, [H|T]) ->
    [CSeq, Method] = re:split(H, " ", [{return, list}]),
    parse_header_values("cseq", R#parse_record{cseq = CSeq, method = Method}, T);
parse_header_values("contact", R, [H|T]) ->
    Contact_Record = parse_contact_values(#contact_record{}, re:split(H, ";", [{return, list}])),
    parse_header_values("contact", R#parse_record{contact = R#parse_record.contact ++ [Contact_Record]}, T);
parse_header_values("authenticate", R, [H|T]) ->
    Parse_Record =
        case re:run(H, " ", []) of
            {match, [{S1, L1}]} ->
                Challenge_Type = string:strip(string:substr(H, 1, S1), left),
                Challenge = re:replace(string:substr(H, S1+L1+1), "\"", "", [global, {return, list}]),
                case Challenge_Type of
                    "Digest" ->
                        parse_digest_string(R, re:split(Challenge, ",", [{return, list}]));
                    _ ->
                        R
                end;
            nomatch ->
                R
        end,
    parse_header_values("www-authenticate", Parse_Record, T);
parse_header_values(_, R, _) ->
    R.

parse_contact_values(R, []) ->
    R;
parse_contact_values(R, [H|T]) ->
    case re:run(H, "<sip:", []) of
        {match, [{S1, _}]} ->
            case re:run(H, ">", []) of
                {match, [{S2, _}]} ->
                    Contact_URI = string:substr(H, S1+1, S2+1-S1),
                    parse_contact_values(R#contact_record{contact = Contact_URI}, T);
                _ ->
                    parse_contact_values(R, T)
            end;
        _ ->
            Key_Value_String = re:split(string:strip(H), "=", [{return, list}]),
            if 
                length(Key_Value_String) == 1 ->
                    parse_contact_values(R, T);
                true ->
                    [Key, Value] = Key_Value_String,
                    case Key of 
                        "expires" ->
                            parse_contact_values(R#contact_record{expires = list_to_integer(Value)}, T);
                        _ ->
                            parse_contact_values(R, T)
                    end
            end
    end.
                    
parse_digest_string(R, []) ->
    R;

parse_digest_string(R, [H|T]) ->
    Key_Value = re:split(string:strip(H, left), "=", [{return, list}]),
    case Key_Value of
        ["realm", Realm] ->
            parse_digest_string(R#parse_record{realm = Realm}, T);
        ["nonce", Nonce] ->
            parse_digest_string(R#parse_record{nonce = Nonce}, T);
        ["opaque", Opaque] ->
            parse_digest_string(R#parse_record{opaque = Opaque}, T);
        ["stale", Stale] ->
            parse_digest_string(R#parse_record{stale = Stale}, T);
        ["qop", QOP] ->
            parse_digest_string(R#parse_record{qop = QOP}, T);
        _ ->
            parse_digest_string(R, T)
    end.

parse_sdp_data(R, [[]]) ->
    R;

parse_sdp_data(R, [H|T]) ->
    [Key, Value|_] = re:split(H, "=", [{return, list}]),
    case Key of
        "o" ->
            [_, _, _, _, _, IP] = re:split(Value, " ", [{return, list}]),
            parse_sdp_data(R#sdp{peer_ip = IP}, T);
        "c" ->
            [_, _, IP] = re:split(Value, " ", [{return, list}]),
            parse_sdp_data(R#sdp{peer_ip = IP}, T);
        "m" ->
            ["audio", Port, _ | Media_List] = re:split(Value, " ", [{return, list}]),
            Media_Format = [ X || X <- Media_List,
                                  lists:member(list_to_integer(X), ?MEDIA_LIST) ],

            if
                length(Media_Format) >= 1 ->
                    Media = hd(Media_Format);
                true ->
                    Media = ""
            end,
            parse_sdp_data(R#sdp{peer_port = list_to_integer(Port), media = Media}, T);
        _ ->
            parse_sdp_data(R, T)
    end.
